using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace GameFrame
{
    [Serializable]
    [StructLayout(LayoutKind.Explicit)]
    public struct Ptr : IEquatable<Ptr>
    {
        public const int SIZE = 4;
        [FieldOffset(0)]
        public int Offset;

        public static Ptr Null
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Ptr();
        }

        public Ptr(int offset) => this.Offset = offset;

        public bool Equals(Ptr other) => this.Offset == other.Offset;

        public override bool Equals(object obj) => obj is Ptr other && this.Equals(other);

        public override int GetHashCode() => this.Offset;

        public override string ToString() => string.Format("<{0}>", (object) this.Offset);

        public static implicit operator bool(Ptr a) => a.Offset != 0;

        public static bool operator ==(Ptr a, Ptr b) => a.Offset == b.Offset;

        public static bool operator !=(Ptr a, Ptr b) => a.Offset != b.Offset;

        public static Ptr operator +(Ptr p, int v) => new Ptr(p.Offset + v);

        public static Ptr operator -(Ptr p, int v) => new Ptr(p.Offset + v);

        public static unsafe void Serialize(void* ptr, FrameSerializer serializer) => serializer.PointerSerialize((Ptr*) ptr);

        public static unsafe void SerializePtrAndData(
            Ptr* ptr,
            FrameSerializer serializer,
            int dataSize)
        {
            FrameSerializer.PtrKey key;
            if (!serializer.PointerSerialize(ptr, out key))
                return;
            byte* buffer = (byte*) serializer.PointerResolve(key, dataSize);
            serializer.Stream.SerializeBuffer(buffer, dataSize);
        }
    }
}